package ch.electronet.wjw.index;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

public class Index<T> implements Serializable {

	private static final long serialVersionUID = 1L;

	private Analyzer analyzer;

	private Node root;

	private String path;

	public Analyzer getAnalyzer() {
		return analyzer;
	}

	public void setAnalyzer(Analyzer analyzer) {
		this.analyzer = analyzer;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public Index() {
		root = new Node();
	}

	public Collection<T> search(String query) {
		final String[] words = query.split("\\s+");
		Collection<T> result = null;
		for (String word : words) {
			SearchVisitor<T> visitor = new SearchVisitor<T>(word, getAnalyzer());
			visitor.visit(root);
			if (result == null) {
				result = visitor.getResult();
			} else {
				result.retainAll(visitor.getResult());
			}
		}
		if (result.size() > 0)
			result = new LinkedList<T>(result).subList(0, Math.min(result.size(), 100));
		return result;
	}

	public void addObject(String token, T object) {
		Analyzer analyzer = getAnalyzer();
		if (analyzer != null) {
			token = analyzer.analyze(token);
		}
		String[] words = token.split("\\W+");
		for (String word : words) {
			root.add(word, object);
		}
	}

	public void reindex(Collection<T> list, TokenMapper<T> mapper) {
		root = new Node();
		for (T item : list) {
			addObject(mapper.getToken(item), item);
		}
	}

	public void load() throws IOException, ClassNotFoundException {
		ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(path)));
		Index index = (Index) in.readObject();
		if (index != null)
			root = index.root;
		in.close();
	}

	public void flush() throws IOException {
		ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(path)));
		out.writeObject(this);
		out.close();
	}

	public void print() {
		root.accept(new PrintVisitor());
	}

	public int count() {
		CounterVisitor visitor = new CounterVisitor();
		root.accept(visitor);
		return visitor.count();
	}

	public interface TokenMapper<T> {
		public String getToken(T item);
	}

}
